package com.sunricher.telinkblemeshlib.db;

import android.content.Context;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.sunricher.telinkblemeshlib.models.RoomScene;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RoomSceneManager {
    private static volatile RoomSceneManager instance;
    private final RoomSceneDatabase database;

    private RoomSceneManager(Context context) {
        database = RoomSceneDatabase.getDatabase(context);
    }

    public static RoomSceneManager getInstance(Context context) {
        if (instance == null) {
            synchronized (RoomSceneManager.class) {
                if (instance == null) {
                    instance = new RoomSceneManager(context);
                }
            }
        }
        return instance;
    }

    public long insert(RoomScene roomScene) {
        return database.insert(roomScene);
    }

    public void insert(List<RoomScene> roomScenes) {
        database.insert(roomScenes);
    }

    public List<RoomScene> selectAll() {
        return database.selectAll();
    }

    public List<RoomScene> selectAllWithRoomId(int roomId) {
        return database.selectAllWithRoomId(roomId);
    }

    public RoomScene select(int roomId, long identifier) {
        return database.select(roomId, identifier);
    }

    public void update(RoomScene roomScene) {
        database.update(roomScene);
    }

    public void update(List<RoomScene> roomScenes) {
        database.update(roomScenes);
    }

    /// You should call this method when you delete a RoomScene.
    public void deleteWithIdentifier(long identifier) {
        database.deleteWithIdentifier(identifier);
    }

    /// You should call this method when you delete a room.
    public void deleteAllWithRoomId(int roomId) {
        database.deleteAllWithRoomId(roomId);
    }

    /// You should call this method when you reset your network or replace your network.
    public void deleteAll() {
        database.deleteAll();
    }

    /// You should call this method when you remove a light in the room.
    public void deleteRoomSceneLights(int roomId, int lightAddress) {
        List<RoomScene> scenes = selectAllWithRoomId(roomId);
        if (scenes.isEmpty()) return;
        List<RoomScene> newScenes = deleteRoomSceneActions(scenes, lightAddress);
        update(newScenes);
    }

    /// You should call this method when you delete a light from the light settings.
    public void deleteRoomSceneLights(int lightAddress) {
        List<RoomScene> scenes = selectAll();
        if (scenes.isEmpty()) return;
        List<RoomScene> newScenes = deleteRoomSceneActions(scenes, lightAddress);
        update(newScenes);
    }

    /// Delete all actions with the same light address.
    List<RoomScene> deleteRoomSceneActions(List<RoomScene> scenes, int lightAddress) {
        List<RoomScene> newScenes = new ArrayList<>();
        if (scenes == null || scenes.isEmpty()) return newScenes;
        for (int i = 0; i < scenes.size(); i++) {
            RoomScene scene = scenes.get(i);
            if (scene.actions == null || scene.actions.isEmpty()) {
                newScenes.add(scene);
                continue;
            }
            List<RoomScene.Action> newActions = new ArrayList<>();
            for (int j = 0; j < scene.actions.size(); j++) {
                RoomScene.Action action = scene.actions.get(j);
                if (action.target != lightAddress) {
                    newActions.add(action);
                }
            }
            scene.actions = newActions;
            newScenes.add(scene);
        }
        return newScenes;
    }

    public String exportRoomScenesToJsonString() {
        List<RoomScene> roomScenes = selectAll();
        List<String> roomScenesStringList = new ArrayList<>();
        for (RoomScene roomScene : roomScenes) {
            roomScenesStringList.add(roomScene.getJsonString());
        }
        Map<String, List<String>> json = new HashMap<>();
        json.put("roomScenes", roomScenesStringList);
        try {
            Gson gson = new Gson();
            return gson.toJson(json);
        } catch (Exception e) {
            Log.e("RoomSceneManager", "exportRoomScenesToJsonString error " + e.getMessage());
        }
        return "{}";
    }

    public void importRoomScenesFromJsonString(String jsonString) {
        deleteAll();
        try {
            Gson gson = new Gson();
            Map<String, List<String>> json = gson.fromJson(jsonString, new TypeToken<Map<String, List<String>>>() {
            }.getType());
            if (!json.containsKey("roomScenes")) return;
            List<String> roomScenesStringList = json.get("roomScenes");
            List<RoomScene> newScenes = new ArrayList<>();
            for (String roomSceneString : roomScenesStringList) {
                RoomScene roomScene = RoomScene.makeWithJsonString(roomSceneString);
                if (roomScene != null) {
                    newScenes.add(roomScene);
                }
            }
            insert(newScenes);
            Log.i("RoomSceneManager", "Imported room scenes " + newScenes.size());
        } catch (Exception e) {
            Log.i("RoomSceneManager", "importRoomScenesFromJsonString error " + e.getMessage());
        }
    }

    public void test() {
        String tag = "testRoomScene";
        RoomScene scene = new RoomScene("s1", 0x8001);
        long insertResult = insert(scene);
        insert(scene);
        insert(scene);
        Log.i(tag, "insertResult " + insertResult);
        scene.identifier = insertResult;
        RoomScene insertedScene = select(0x8001, insertResult);
        Log.i(tag, "insertedScene " + insertedScene.name + ", identifier " + insertedScene.identifier);
        List<RoomScene> scenes = selectAllWithRoomId(0x8001);
        Log.i(tag, "scenes " + scenes.size());

        scene.actions.add(new RoomScene.Action(0x8001));
        scene.actions.add(new RoomScene.Action(0x8001));
        scene.actions.add(new RoomScene.Action(0x8002));
        scene.actions.add(new RoomScene.Action(0x8003));
        update(scene);
        List<RoomScene> newScenes = selectAllWithRoomId(0x8001);
        RoomScene newScene = newScenes.get(0);
        Log.i(tag, "newScene action " + newScene.actions.size());

        String exported = exportRoomScenesToJsonString();
        Log.i(tag, "exported " + exported);
        importRoomScenesFromJsonString(exported);
        scenes = selectAllWithRoomId(0x8001);
        Log.i(tag, "scenes " + scenes.size());

        deleteRoomSceneLights(0x8002);
        newScenes = selectAllWithRoomId(0x8001);

        if (newScenes.isEmpty()) return;
        newScene = newScenes.get(0);
        Log.i(tag, "newScene action after delete target 0x8002: " + newScene.actions.size());
        deleteRoomSceneLights(0x8002, 0x8001);
        newScenes = selectAllWithRoomId(0x8001);
        newScene = newScenes.get(0);
        Log.i(tag, "newScene action after delete 0x8002, 0x8001: " + newScene.actions.size());
        newScenes = selectAll();
        Log.i(tag, "selectAll size " + newScenes.size() + ", first " + newScenes.get(0).roomId);
        deleteWithIdentifier(newScenes.get(0).identifier);
        newScenes = selectAll();
        Log.i(tag, "selectAll size after delete identifier " + newScenes.size());
        deleteAllWithRoomId(0x8001);
        scenes = selectAllWithRoomId(0x8001);
        Log.i(tag, "scenes after deleteAllWithRoomId " + scenes.size());
        deleteAll();
        Log.i(tag, "after deleteAll size " + selectAll().size());
    }
}
